﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEditor.Experimental.GraphView;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;

namespace SkillEditor
{
    public class SkillEditorGraphNode : Node
    {
        public SkillEditorNodeInfo info;

        public SkillNode data;
        public SkillSpace space;

        public void SetData(SkillSpace space, SkillNode data)
        {
            this.space = space;
            this.data = data;

            SetPosition(new Rect(data.x, data.y, 0, 0));

            foreach (var item in data.dataKey)
            {
                if (space.dataInt.ContainsKey(item.Value))
                {
                    SetKeyValue(item.Key, space.dataInt[item.Value]);
                }
                else if (space.dataString.ContainsKey(item.Value))
                {
                    SetKeyValue(item.Key, space.dataString[item.Value]);
                }
                else if (space.dataBool.ContainsKey(item.Value))
                {
                    SetKeyValue(item.Key, space.dataBool[item.Value]);
                }
            }
        }

        public void SetInfo(SkillEditorNodeInfo v)
        {
            this.info = v;
            ShowInfo();
        }

        private void ShowInfo()
        {
            title = info.id;

            if (info.type == SkillEditorNodeType.Action)
            {
                ShowAction();
            }

            for (int i = 0; i < info.ports.Length; i++)
            {
                SkillEditorPortInfo portInfo = info.ports[i];

                switch (portInfo.valueType)
                {
                    case SkillEditorValueType.Bool:
                        if (portInfo.type == SkillEditorPortType.Input)
                            AddBoolInPort(portInfo.name);
                        else
                            AddBoolOutPort(portInfo.name);
                        break;

                    case SkillEditorValueType.Damage:
                        if (portInfo.type == SkillEditorPortType.Input)
                            AddDamageInPort(portInfo.name);
                        else
                            AddDamageOutPort(portInfo.name);
                        break;

                    case SkillEditorValueType.Float:
                        //if (portInfo.type == SkillEditorPortType.Input)
                        //    addf(portInfo.name);
                        //else
                        //    AddBoolOutPort(portInfo.name);
                        break;

                    case SkillEditorValueType.Int:
                        if (portInfo.type == SkillEditorPortType.Input)
                            AddIntInPort(portInfo.name);
                        else
                            AddIntOutPort(portInfo.name);
                        break;

                    case SkillEditorValueType.Step:
                        if (portInfo.type == SkillEditorPortType.Input)
                        {
                            var intputPort = Port.Create<Edge>(Orientation.Vertical, Direction.Input, Port.Capacity.Multi, typeof(StructStep));
                            intputPort.portName = portInfo.name;
                            titleContainer.Add(intputPort);
                        }
                        else
                        {
                            var outputPort = Port.Create<Edge>(Orientation.Horizontal, Direction.Output, Port.Capacity.Single, typeof(StructStep));
                            outputPort.portName = portInfo.name;
                            outputContainer.Add(outputPort);
                        }
                        break;

                    case SkillEditorValueType.String:
                        if (portInfo.type == SkillEditorPortType.Input)
                            AddStringInPort(portInfo.name);
                        else
                            AddStringOutPort(portInfo.name);
                        break;

                    case SkillEditorValueType.Targets:
                        if (portInfo.type == SkillEditorPortType.Input)
                            AddTargetsInPort(portInfo.name);
                        else
                            AddTargetsOutPort(portInfo.name);
                        break;
                }
            }
        }

        private void ShowAction()
        {
            var intputPort = Port.Create<Edge>(Orientation.Vertical, Direction.Input, Port.Capacity.Multi, typeof(StructStep));
            intputPort.portName = "Previous";
            titleContainer.Add(intputPort);

            var outputPort = Port.Create<Edge>(Orientation.Horizontal, Direction.Output, Port.Capacity.Single, typeof(StructStep));
            outputPort.portName = "Next";
            outputContainer.Add(outputPort);

            titleContainer.style.backgroundColor = new Color(0.83f, 0.22f, 0.22f, 1f);
        }

        public void AddBoolInPort(string portName, bool enableWrite = true)
        {
            Port port = Port.Create<Edge>(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, typeof(bool));
            port.portName = portName;
            inputContainer.Add(port);

            if (enableWrite)
            {
                Toggle toggle = new Toggle();
                port.Add(toggle);
            }
        }

        public void AddBoolOutPort(string portName)
        {
            Port port = Port.Create<Edge>(Orientation.Horizontal, Direction.Output, Port.Capacity.Multi, typeof(bool));
            port.portName = portName;
            outputContainer.Add(port);
        }

        public void AddIntInPort(string portName, bool enableWrite = true)
        {
            Port port = Port.Create<Edge>(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, typeof(int));
            port.portName = portName;
            inputContainer.Add(port);
            if (enableWrite)
            {
                IntegerField field = new IntegerField { value = 0 };
                port.Add(field);
            }
        }

        public void AddIntOutPort(string portName)
        {
            Port port = Port.Create<Edge>(Orientation.Horizontal, Direction.Output, Port.Capacity.Multi, typeof(int));
            port.portName = portName;
            outputContainer.Add(port);
        }

        public void AddStringInPort(string portName, bool enableWrite = true)
        {
            Port port = Port.Create<Edge>(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, typeof(string));
            port.portName = portName;
            inputContainer.Add(port);
            if (enableWrite)
            {
                TextField field = new TextField { value = "" };
                port.Add(field);
            }
        }

        public void AddStringOutPort(string portName)
        {
            Port port = Port.Create<Edge>(Orientation.Horizontal, Direction.Output, Port.Capacity.Multi, typeof(string));
            port.portName = portName;
            outputContainer.Add(port);
        }

        public void AddTargetsInPort(string portName)
        {
            Port port = Port.Create<Edge>(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, typeof(StructListTarget));
            port.portName = portName;
            inputContainer.Add(port);
        }

        public void AddTargetsOutPort(string portName)
        {
            Port port = Port.Create<Edge>(Orientation.Horizontal, Direction.Output, Port.Capacity.Multi, typeof(StructListTarget));
            port.portName = portName;
            outputContainer.Add(port);
        }

        public void AddDamageInPort(string portName)
        {
            Port port = Port.Create<Edge>(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, typeof(StructDamage));
            port.portName = portName;
            inputContainer.Add(port);
        }

        public void AddDamageOutPort(string portName)
        {
            Port port = Port.Create<Edge>(Orientation.Horizontal, Direction.Output, Port.Capacity.Multi, typeof(StructDamage));
            port.portName = portName;
            outputContainer.Add(port);
        }

        public void AddInPort(string portName, Type type)
        {
            Port port = Port.Create<Edge>(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, type);
            port.portName = portName;
            inputContainer.Add(port);
        }

        public void AddOutPort(string portName, Type type)
        {
            var port = Port.Create<Edge>(Orientation.Horizontal, Direction.Output, Port.Capacity.Multi, type);
            port.portName = portName;
            port.name = SkillEditorSpaceWindow.GuidTo16String();
            outputContainer.Add(port);
        }

        public Port GetPort(string name)
        {
            foreach (var item in inputContainer.Children())
            {
                if (item is Port port && port.portName == name)
                {
                    return port;
                }
            }
            foreach (var item in outputContainer.Children())
            {
                if (item is Port port && port.portName == name)
                {
                    return port;
                }
            }
            foreach (var item in titleContainer.Children())
            {
                if (item is Port port && port.portName == name)
                {
                    return port;
                }
            }
            return null;
        }

        public void SetKeyValue(string key, int value)
        {
            Port port = GetPort(key);

            if (port == null)
                return;

            foreach (var input in port.Children())
            {
                if (input is IntegerField field)
                {
                    field.value = value;
                }
            }
        }

        public void SetKeyValue(string key, string value)
        {
            Port port = GetPort(key);
            if (port == null)
            {
                Debug.LogError("not find port " + key + "=" + value);
                return;
            }

            foreach (var input in port.Children())
            {
                if (input is TextField field)
                {
                    field.value = value;
                }
            }
        }

        public void SetKeyValue(string key, bool value)
        {
            Port port = GetPort(key);
            foreach (var input in port.Children())
            {
                if (input is Toggle field)
                {
                    field.value = value;
                }
            }
        }

        internal SkillNode GetCopyData()
        {
            GetDataAndUpdate();

            SkillNode skillNode = new SkillNode()
            {
                id = "",
                type = data.type,
                x = data.x + 200,
                y = data.y + 200,                
            };

            foreach (var item in inputContainer.Children())
            {
                if (item is Port port)
                {
                    string key = port.portName;

                    foreach (var input in port.Children())
                    {
                        if (input is IntegerField integerField)
                        {
                            if (integerField.value != 0)
                            {
                                skillNode.dataKey[key] = SkillEditorSpaceWindow.GuidTo16String();
                                space.dataInt[skillNode.dataKey[key]] = integerField.value;
                            }
                        }
                        else if (input is TextField textField)
                        {
                            if (string.IsNullOrEmpty(textField.text) == false)
                            {
                                skillNode.dataKey[key] = SkillEditorSpaceWindow.GuidTo16String();
                                space.dataString[skillNode.dataKey[key]] = textField.text;
                            }
                        }
                        else if (input is Toggle toggle)
                        {
                            if (toggle.value)
                            {
                                skillNode.dataKey[key] = SkillEditorSpaceWindow.GuidTo16String();
                                space.dataBool[skillNode.dataKey[key]] = toggle.value;
                            }
                        }
                    }
                }
            }



            return skillNode;
        }

        internal SkillNode GetDataAndUpdate()
        {
            data.x = layout.position.x;
            data.y = layout.position.y;
            data.dataKey.Clear();

            foreach (var item in inputContainer.Children())
            {
                if (item is Port port)
                {
                    string key = port.portName;

                    foreach (var input in port.Children())
                    {
                        if (input is IntegerField integerField)
                        {
                            if (integerField.value != 0)
                            {
                                data.dataKey[key] = SkillEditorSpaceWindow.GuidTo16String();
                                space.dataInt[data.dataKey[key]] = integerField.value;
                            }
                        }
                        else if (input is TextField textField)
                        {
                            if (string.IsNullOrEmpty(textField.text) == false)
                            {
                                data.dataKey[key] = SkillEditorSpaceWindow.GuidTo16String();
                                space.dataString[data.dataKey[key]] = textField.text;
                            }
                        }
                        else if (input is Toggle toggle)
                        {
                            if (toggle.value)
                            {
                                data.dataKey[key] = SkillEditorSpaceWindow.GuidTo16String();
                                space.dataBool[data.dataKey[key]] = toggle.value;
                            }
                        }
                    }
                }
            }

            foreach (var item in outputContainer.Children())
            {
                if (item is Port port && port.connected)
                {
                    if (port.portType == typeof(StructStep))
                    {
                        continue;
                    }
                    else
                    {
                        string key = port.portName;
                        data.dataKey[key] = SkillEditorSpaceWindow.GuidTo16String();
                    }
                }
            }

            foreach (var item in titleContainer.Children())
            {
                if (item is Port port && port.connected)
                {
                    if (port.portType == typeof(StructStep))
                    {
                        continue;
                    }
                    else
                    {
                        string key = port.portName;
                        data.dataKey[key] = SkillEditorSpaceWindow.GuidTo16String();
                    }
                }
            }

            return data;
        }

        public override void OnUnselected()
        {
            base.OnUnselected();

            using (var e = new ChangeDataEvent() { target = this, data = null })
            {
                SendEvent(e);
            }
        }

        public override void OnSelected()
        {
            base.OnSelected();
            using (var e = new ChangeDataEvent() { target = this, data = this })
            {
                SendEvent(e);
            }

            foreach (var item in inputContainer.Children())
            {
                if (item is Port port)
                {
                    bool isHide = !port.connected;
                    foreach (var input in port.Children())
                    {
                        if (input is IntegerField integerField)
                        {
                            input.visible = isHide;
                        }
                        else if (input is TextField textField)
                        {
                            input.visible = isHide;
                        }
                        else if (input is Toggle toggle)
                        {
                            input.visible = isHide;
                        }
                    }
                }
            }
        }

        public virtual string GetDesc()
        {
            return info.desc;
        }
    }
}